<?php
include_once 'Facebook/facebook.php';

class YouNet_Facebook_Api extends Facebook
{
    /**
    * Signed Request Algorithm.
    */
    const SIGNED_REQUEST_ALGORITHM = 'HMAC-SHA256';
  
	protected $_user;
	protected $_extended_access_token;	
	
	public function __construct($config) 
	{
		parent::__construct($config);
		if (isset($config['proxy.host']) && isset($config['proxy.port']))
		{
			self::$CURL_OPTS[CURLOPT_HTTPPROXYTUNNEL] = 'true';
			self::$CURL_OPTS[CURLOPT_PROXY] = $config['proxy.host'];
			self::$CURL_OPTS[CURLOPT_PROXYPORT] = $config['proxy.port'];
		}
	}
    /**
       * Base64 encoding that doesn't need to be urlencode()ed.
       * Exactly the same as base64_encode except it uses
       *   - instead of +
       *   _ instead of /
       *
       * @param string $input string
       * @return string base64Url encoded string
       */
      protected static function base64UrlEncode($input) {
        $str = strtr(base64_encode($input), '+/', '-_');
        $str = str_replace('=', '', $str);
        return $str;
      }
    /**
   * Makes a signed_request blob using the given data.
   *
   * @param array The data array.
   * @return string The signed request.
   */
    protected function makeSignedRequest($data) {
        if (!is_array($data)) {
        throw new InvalidArgumentException(
          'makeSignedRequest expects an array. Got: ' . print_r($data, true));
        }
        $data['algorithm'] = self::SIGNED_REQUEST_ALGORITHM;
        $data['issued_at'] = time();
        $json = json_encode($data);
        $b64 = self::base64UrlEncode($json);
        $raw_sig = hash_hmac('sha256', $b64, $this->getApiSecret(), $raw = true);
        $sig = self::base64UrlEncode($raw_sig);
        return $sig.'.'.$b64;
    }
	
	public function getSignedRequest() {
		if (! $this->signedRequest) {
			if (isset ( $_REQUEST ['signed_request'] )) {                
				$this->signedRequest = $this->parseSignedRequest ( $_REQUEST ['signed_request'] );
				$base_url	= Zend_Controller_Front::getInstance()->getBaseUrl();
				$module		= Zend_Controller_Front::getInstance()->getRequest()->getModuleName();
                $oldSignedRequestData = array();
                if (isset ( $_COOKIE [$this->getSignedRequestCookieName ()] )) {
                    $oldSignedRequestData = $this->parseSignedRequest ( $_COOKIE [$this->getSignedRequestCookieName ()] );
                }
				setcookie($this->getSignedRequestCookieName (), $_REQUEST ['signed_request'], 0, $base_url);
				//For new apps - with "tab" router
				if( in_array($module,array('tab', 'page')) )
				{
                    $id_page = @$this->signedRequest['page']['id'];
                    if( !$id_page )
                    {
                        $id_page = Zend_Controller_Front::getInstance()->getRequest()->getParam('id_page');
                    }
					if ($id_page)
					{
						$path		= $base_url . '/' . $module.'/'.$id_page;
                        if( isset($oldSignedRequestData['oauth_token']) && isset($this->signedRequest['code']) ){
                            unset($oldSignedRequestData['oauth_token']);
                        }
                        if( !isset($this->signedRequest['user_id']) ){
                            unset($oldSignedRequestData['user_id']);
                            unset($oldSignedRequestData['code']);
                            unset($oldSignedRequestData['oauth_token']);
                        }
                        $this->signedRequest = array_merge($oldSignedRequestData, $this->signedRequest);
                        $newSignedRequest = $this->makeSignedRequest($this->signedRequest);
						setcookie($this->getSignedRequestCookieName (), $newSignedRequest, 0, $path);
					}
				}
			} else if (isset ( $_COOKIE [$this->getSignedRequestCookieName ()] )) {
				$this->signedRequest = $this->parseSignedRequest ( $_COOKIE [$this->getSignedRequestCookieName ()] );
			}
		}		
		return $this->signedRequest;
	}
	
	public function getOauthUrl($redirect_url, $oauth_scopes)
	{
		$this->establishCSRFTokenState();
		return "https://www.facebook.com/dialog/oauth?client_id=".$this->getAppId()
			."&redirect_uri=".rawurlencode($redirect_url)
			."&state=".$this->state."&scope=".$oauth_scopes;
	}
		
	protected function getUserAccessToken() 
	{
		//first, try to get access_token from $_REQUEST in case of testing
		if (isset($_REQUEST['access_token']))
		{
			$access_token = $_REQUEST['access_token'];
	        $this->setPersistentData('access_token', $access_token);
	        return $access_token;
		}
		else 
		{
			return parent::getUserAccessToken();
		}
	}
	
	public function getExtendedAccessToken()
	{	
		try {
	        // need to circumvent json_decode by calling _oauthRequest
	          // directly, since response isn't JSON format.
	        $access_token_response =
	            $this->_oauthRequest(
	                $this->getUrl('graph', '/oauth/access_token'), array(
	                    'client_id' => $this->getAppId(),
	                    'client_secret' => $this->getApiSecret(),
	                    'grant_type'=>'fb_exchange_token',
	                    'fb_exchange_token'=>$this->accessToken,
	                	'access_token'=>$this->accessToken
	                )
	            );
	    } catch (FacebookApiException $e) {
	      // most likely that user very recently revoked authorization.
	      // In any event, we don't have an access token, so say so.
	      return false;
	    }
	
	    if (empty($access_token_response)) {
	      return false;
	    }
	
	    $response_params = array();
	    parse_str($access_token_response, $response_params);
	    if (!isset($response_params['access_token'])) {
	      return false;
	    }
	
	    return $response_params['access_token'];
	}
	
	// @Override
	public function getAccessToken() 
	{
		if ($this->_extended_access_token !== null) {
	    	// we've done this already and cached it.  Just return.
	    	return $this->_extended_access_token;
	    }
	    $access_token = '';
		//Has new access token, correct even if at first time
		if( $this->hasNewSignedRequest() )
		{
			$access_token			= parent::getAccessToken();//called: setPersistentData('access_token')
			//Fails, if user open another browser and call to get extended_access_token
			//Because a access token can not be extended 2 more times.
			$extended_access_token	= $this->getExtendedAccessToken();
			if( $extended_access_token )
			{
				$this->setExtendedAccessToken($extended_access_token);
				$this->setPersistentData('extended_access_token', $extended_access_token);
				YouNet_Model_DbTable_Users::updateExtendedAccessToken($this->user, $extended_access_token);
				return $extended_access_token;
			}
		}
		$extended_access_token = '';
		if( $this->user )
		{
			$extended_access_token = YouNet_Model_DbTable_Users::getExtendedAccessToken($this->user);
		}
		if( $extended_access_token )
		{
			$this->setExtendedAccessToken($extended_access_token);
			return $extended_access_token;
		}

		if( !$access_token )
		{
			return parent::getAccessToken();
		}
		return $access_token;
	}
	
	public function setExtendedAccessToken($extended_access_token) {
		$this->_extended_access_token = $extended_access_token;
		return $this;
	}

	public function hasNewSignedRequest()
	{
		if( isset($_REQUEST ['signed_request']) )
		{
			$signed_request = $this->parseSignedRequest($_REQUEST['signed_request']);			
			
			if( empty($signed_request['expires']) )
			//In case: the app is removed by user and push from top
			{
				return true;
			}
			else
			{
				if( !empty($signed_request['oauth_token']) )
				{
					if( ($signed_request['expires'] > time())
					&& ($signed_request['oauth_token'] != $this->getPersistentData('access_token')) )
					{
						return true;
					}
				}
			}			
		}
		return false;
	}
	
	public function getFanPageInfo($id_page)
	{
		$key = 'FANPAGE_INFO' . $id_page;
		$fanpage = YouNet_Util::getObjectMemCache()->load($key);

		if ($fanpage === false)
		{
			try
			{
				$fanpage = $this->api('/' . $id_page, array(
					'access_token' => $this->getApplicationAccessToken()
				));
			}
			catch (Exception $e)
			{
				$fanpage = $this->api('/' . $id_page, array(
					'access_token' => YouNet_Model_DbTable_Users::getLatestExtendedAccessToken()
				));
			}
			YouNet_Util::getObjectMemCache()->save($fanpage, $key, array(), 86400);
		}
		return $fanpage;
	}
	
	// @Override
//	protected function getUserFromAvailableData()
//	{
//		if (!$this->_user)
//		{
//		    // use access_token to fetch user id if we have a user access_token, or if
//		    // the cached access token has changed.
//		    $access_token = $this->getAccessToken();
//		    if ($access_token &&
//		        $access_token != $this->getApplicationAccessToken()) {
//		      $this->_user = $this->getUserFromAccessToken();
//		      if (!$this->_user) {
//		        $this->clearAllPersistentData();
//		      }
//		    }
//		}
//	    return $this->_user;
//	}
//	
//	protected function makeRequest($url, $params, $ch=null) 
//	{
//		$time_start = time();
//		try 
//		{
//			parent::makeRequest($url, $params, $ch);
//		} 
//		catch (Exception $e)
//		{
//			$time_end = time();
//			$time_diff = $time_end - $time_start;
//			error_log(date('Y-m-d H:i:s') . " | URL::$url | Params::$params | Error: $e | Time::$time_diff\n" , 3, 'facebook.log');
//			throw $e;			
//		} 
//		
//		$time_end = time();
//		$time_diff = $time_end - $time_start;
//		error_log(date('Y-m-d H:i:s') . " | URL::$url | Params::$params | Time::$time_diff\n" , 3, 'facebook.log');
//	}
}